package org.iabako.client.ui.form.business.sale.make;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.HTMLPanel;
import org.iabako.client.config.menu.MenuItemEnum;
import org.iabako.client.config.page.PageConfigEnum;
import org.iabako.client.config.page.PageEnum;
import org.iabako.client.navigation.NavigationController;
import org.iabako.client.tools.IabakoAsyncCallback;
import org.iabako.client.tools.Messages;
import org.iabako.client.ui.form.AbstractForm;
import org.iabako.client.ui.layout.field.Button;
import org.iabako.client.ui.layout.field.IField;
import org.iabako.client.ui.layout.field.LabelText;
import org.iabako.client.ui.layout.template.Menu;
import org.iabako.client.ui.layout.template.Popup;
import org.iabako.client.ui.layout.widget.dialogbox.IabakoDialogBox;
import org.iabako.client.ui.layout.widget.sale.SaleProductServiceInfoLine;
import org.iabako.client.ui.layout.widget.sale.SaleWorkflow;
import org.iabako.client.ui.layout.widget.validation.ValidationMessage;
import org.iabako.client.ui.page.business.sale.QuoteMake;
import org.iabako.client.ui.page.business.sale.QuoteModify;
import org.iabako.client.ui.page.business.sale.SaleMake;
import org.iabako.client.ui.page.business.sale.SaleSearch;
import org.iabako.client.user.AuthenticateUserProvider;
import org.iabako.shared.entity.business.*;
import org.iabako.shared.entity.enumeration.*;
import org.iabako.shared.iservice.business.QuoteServiceAsync;
import org.iabako.shared.iservice.business.SaleEnMasseServiceAsync;
import org.iabako.shared.iservice.business.SaleServiceAsync;
import org.iabako.shared.querydefinition.columndefinition.ClientColumnEnum;
import org.iabako.shared.querydefinition.dto.QueryResultDTO;
import org.iabako.shared.tools.GenericTools;

import java.util.*;

/**
 * Created by jose on 20/07/14.
 */
public abstract class SaleMakeForm extends AbstractForm {

    interface MyUiBinder extends UiBinder<HTMLPanel, SaleMakeForm> {
    }

    protected MyUiBinder binderSalesForm = GWT.create(MyUiBinder.class);

    protected final QuoteServiceAsync quoteServiceAsync = QuoteServiceAsync.Util.getInstance();
    protected final SaleServiceAsync saleServiceAsync = SaleServiceAsync.Util.getInstance();
    protected final SaleEnMasseServiceAsync saleEnMasseServiceAsync = SaleEnMasseServiceAsync.Util.getInstance();

    @UiField
    SaleWorkflow saleWorkflow;
    @UiField
    public SaleMakeClientChoice saleMakeClientChoice;
    @UiField
    SaleMakeProductServiceChoice saleMakeProductServiceChoice;
    @UiField
    SaleMakePaymentDefinition saleMakePaymentDefinition;
    @UiField
    public SaleMakeConfirmation saleMakeConfirmation;
    @UiField
    LabelText saleDraftAction;
    @UiField
    LabelText quoteDownloadAction;
    @UiField
    LabelText quoteSendEmailAction;
    @UiField
    Button transformQuoteToSale;
    @UiField
    Button discardQuote;
    @UiField
    Button recoverQuote;
    @UiField
    protected HTMLPanel waitIconPanel;
    @UiField
    protected HTMLPanel contentPanel;

    boolean quoteMode;
    boolean quoteTransformToSaleMode;
    boolean saleEnMasse;
    Map<Long, QueryResultDTO> clientList;
    Client client;
    List<SaleProductServiceInfoLine> productServiceList;

    protected SaleMakeForm(PageConfigEnum pageConfigEnum, String explanationMenuLabel) {

        setForm(binderSalesForm, pageConfigEnum, explanationMenuLabel);

        Popup.resizeToMediumPopup();

        popup.actionButton.setVisible(false);

        saleMakeClientChoice.setSaleMakeForm(this);
        saleMakeProductServiceChoice.setSaleMakeForm(this);
        saleMakePaymentDefinition.setSaleMakeForm(this);
        saleMakeConfirmation.setSaleMakeForm(this);
        productServiceList = new ArrayList<SaleProductServiceInfoLine>();
        clientList = new HashMap<Long, QueryResultDTO>();

        goTo(SaleStep.who);

        saleMakeConfirmation.saleDate.setValue(new Date());

        addToFooterCustomActionPanel(saleDraftAction);

        setDraftHandler();
    }

    private void setDraftHandler() {
        saleDraftAction.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {

                //Set sale's ProductServices
                if (productServiceList.isEmpty()) {
                    for (SaleProductServiceInfoLine saleProductServiceInfoLine : saleMakeProductServiceChoice.getProductServicesLine()) {
                        productServiceList.add(saleProductServiceInfoLine.customClone());
                    }
                }

                validate(true);
            }
        });
    }

    public void restoreDraft(Sale sale) {
        entity = sale;
        saleMakeConfirmation.number.setValue(((Sale) entity).getNumber());
        saleMakeConfirmation.saleDate.setValue(GenericTools.getDateFromString(((Sale) entity).getDateString()));
        if (sale.getSaleStep() != SaleStep.confirmation) {
            contentPanel.setVisible(true);
            waitIconPanel.setVisible(false);
            //If quote, saleWorkflow is hidden by default => confirmation.enableModificationMode(false)
            saleWorkflow.setVisible(true);
        }

        //restoreDraft of WHO and WHAT can be done directly here
        //restoreDraft of HOW and CONFIRMATION must be done in cascade mode
        saleMakeClientChoice.restoreDraft();
        saleMakeProductServiceChoice.restoreDraft();

        goTo(sale.getSaleStep());
    }

    protected void activateQuoteMode() {
        quoteMode = true;

        popup.actionButton.getButton().setStyleName(popup.actionButton.getButton().getStyleName() + " inputsize35 fontsize13");
        saleWorkflow.activateQuoteMode();
        saleMakeClientChoice.activateQuoteMode();
        saleMakeProductServiceChoice.activateQuoteMode();
        saleMakeConfirmation.activateQuoteMode();

        addToFooterCustomActionPanel(quoteDownloadAction);
        addToFooterCustomActionPanel(quoteSendEmailAction);
        addToActionCustomButtons(transformQuoteToSale);
        addToActionCustomButtons(discardQuote);
        addToActionCustomButtons(recoverQuote);
    }

    protected void activateSaleEnMasse(String clientListToken) {
        saleEnMasse = true;
        saleEnMasseServiceAsync.getClients(clientListToken, new IabakoAsyncCallback<Map<Long, QueryResultDTO>>() {
            public void onSuccess(Map<Long, QueryResultDTO> result) {
                clientList = result;
                saleMakeClientChoice.activateSaleEnMasse();
                saleMakeConfirmation.activateSaleEnMasse();
                super.onSuccess(result);
            }
        });
    }

    public void showDialogBoxWithClientList() {
        String textClientDetails = "\n";
        for (Long idClient : clientList.keySet()) {
            textClientDetails += "- " + clientList.get(idClient).getValues().get(ClientColumnEnum.name).getValue();
            textClientDetails += " (" + clientList.get(idClient).getValues().get(ClientColumnEnum.email).getValue() + ")\n";
        }
        IabakoDialogBox iabakoDialogBox = new IabakoDialogBox(
                Messages.getLabel("sales_en_masse_client_list_title", clientList.size() + ""), textClientDetails, true);
        iabakoDialogBox.setCloseOnlySimplePopup(true);

        NavigationController.getInstance().show(iabakoDialogBox);
    }

    public void goTo(SaleStep saleStep) {

        saleDraftAction.setVisible(!saleEnMasse);
        saleMakeClientChoice.setVisible(false);
        saleMakeProductServiceChoice.setVisible(false);
        saleMakePaymentDefinition.setVisible(false);
        saleMakeConfirmation.setVisible(false);
        popup.actionButton.setVisible(false);

        //For quote mode
        quoteDownloadAction.setVisible(false);
        quoteSendEmailAction.setVisible(false);
        transformQuoteToSale.setVisible(false);
        discardQuote.setVisible(false);

        saleWorkflow.setSelected(saleStep);

        switch (saleStep) {
            case who:
                saleMakeClientChoice.setVisible(true);
                break;
            case what:
                saleMakeProductServiceChoice.setVisible(true);
                break;
            case how:
                saleMakePaymentDefinition.setVisible(true);
                break;
            case confirmation:
                saleMakeConfirmation.setVisible(true);
                if (quoteMode) {
                    quoteDownloadAction.setVisible(true);
                    quoteSendEmailAction.setVisible(true);
                    saleDraftAction.setVisible(false);
                }
                break;
        }
    }

    public void setSaleWithNoClient(boolean noClient) {
        if (!quoteMode) {
            saleMakeClientChoice.noClientPaymentNote.setVisible(noClient);
            saleMakeProductServiceChoice.noClientPaymentNote.setVisible(noClient);
        }
        saleMakeProductServiceChoice.productServiceLine.setSaleWithClient(!noClient);

        saleMakePaymentDefinition.paymentType.setValue(noClient ? PaymentType.onePayment : null);
        saleMakePaymentDefinition.paymentType.setEnabled(!noClient);
        saleMakePaymentDefinition.paymentMethod.setVisible(noClient);

        saleMakePaymentDefinition.saleNote.setVisible(noClient);
    }


    @Override
    protected List<IField> getMandatoryFields() {
        List<IField> mandatoryFields = new ArrayList<IField>();
        mandatoryFields.add(saleMakeConfirmation.number);
        mandatoryFields.add(saleMakeConfirmation.saleDate);

        return mandatoryFields;
    }

    @Override
    protected AbstractEntity getEntity() {
        if (entity == null) {
            entity = new Sale();
        }
        Sale sale = (Sale) entity;
        sale.setNumber(saleMakeConfirmation.number.getValue());

        PaymentType paymentType = (PaymentType) saleMakePaymentDefinition.paymentType.getValue();
        sale.setPaymentType(paymentType == null ? PaymentType.installments : paymentType);
        sale.setPaymentMethod((PaymentMethod) saleMakePaymentDefinition.paymentMethod.getEnumValue());

        saleMakeProductServiceChoice.updateProductServiceList();
        List<SaleProductService> productsServices = getSaleProductServiceList();

        sale.setProductsServices(productsServices);
        sale.setClient((Client) saleMakeClientChoice.clientSuggestSelectionHandler.getEntity());
        sale.setDate(saleMakeConfirmation.saleDate.getDateValue());
        sale.setDateString(GenericTools.formatDateToString(saleMakeConfirmation.saleDate.getDateValue()));

        if (saleMakeProductServiceChoice.ponderedTax != 0) {
            sale.setDisableTax(saleMakeProductServiceChoice.saleTotalTable.isTaxDisabled());
        } else {
            sale.setDisableTax(null);
        }

        if (saleMakePaymentDefinition.paymentType.getValue() == PaymentType.onePayment) {

            Payment payment = new Payment();
            payment.setDateString(GenericTools.formatDateToString(saleMakeConfirmation.saleDate.getDateValue()));
            payment.setAmount(saleMakeProductServiceChoice.totalPrice);
            payment.setSale(sale);

            sale.getPayments().clear();
            sale.getPayments().add(payment);

        } else {
            InstallmentsDefinition installmentsDefinition = saleMakePaymentDefinition.installmentsForm.getEntity();
            sale.setInstallmentsDefinition(installmentsDefinition);
            for (Installment i : installmentsDefinition.getInstallments()) {
                i.setSale(sale);
            }
        }

        return sale;
    }

    public List<SaleProductService> getSaleProductServiceList() {
        List<SaleProductService> productsServices = new ArrayList<SaleProductService>();
        for (SaleProductServiceInfoLine line : productServiceList) {
            if (line.quantity.getDoubleValue() == null
                    || line.price.getAmountValue() == null
                    || (line.getService() == null && line.getProduct() == null)) {
                //This should be a draft Sale
                continue;
            }

            SaleProductService productService = new SaleProductService();
            productService.setSale((Sale) entity);
            productService.setQuantity(line.quantity.getDoubleValue());
            productService.setPrice(line.price.getAmountValue());
            productService.setPriceUnit(line.priceUnitEnum);
            productService.setTaxPercent(saleMakeProductServiceChoice.saleTotalTable.taxDisabled ? null : line.taxPercent);
            productService.setService(line.getService());
            productService.setProduct(line.getProduct());
            productsServices.add(productService);
        }
        return productsServices;
    }

    @Override
    public boolean validate() {
        //Quote are always saved as draft
        return validate(quoteMode);
    }

    public boolean validate(boolean isDraft) {
        if (!uiValidation()) {
            return false;
        }
        final Sale sale = (Sale) serverValidation();
        if (sale == null) {
            return false;
        }

        if (saleEnMasse) {
            return saveEnMasseProcess(sale);
        } else {
            return normalSaveProcess(sale, isDraft);
        }
    }

    private boolean normalSaveProcess(final Sale sale, boolean isDraft) {

        if (isDraft) {
            sale.setSaleStep(saleWorkflow.getSelectedSaleStep());
            if (saleMakeClientChoice.saleWithNoClient.getValue() && sale.getSaleStep() == SaleStep.who) {
                //Go forward
                sale.setSaleStep(SaleStep.what);
            }

            if (sale.getInstallmentsDefinition() != null
                    && sale.getInstallmentsDefinition().getInstallmentsFrequency() == InstallmentsFrequency.custom
                    && sale.getInstallmentsDefinition().getInstallments().isEmpty()) {
                saleMakePaymentDefinition.installmentsForm.getEntity();
            }

            if (quoteMode && entity != null && entity.getId() == null && ((Sale) entity).getStatus() == FinancialStatusSale._1_quote_generated) {
                //Sale has already been saved by the generate PDF service. We can stop this action.
                popup.handleClickClose();
                ValidationMessage.getFromRootPage().showSuccessMessage("validation_sale_form_quote_save_success");
                AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.quoteDraft);
                return true;
            }
        }

        if (parentForm instanceof QuoteMake || parentForm instanceof QuoteModify) {
            //If it's a Sale created from a quote => Force persist!
            sale.setId(null);
            //Do not go back to Quote form
            parentForm = null;
        }

        List<Installment> installments = sale.getInstallmentsDefinition() != null ? sale.getInstallmentsDefinition().getInstallments() : null;

        if (quoteMode) {
            sale.setSaleStep(saleWorkflow.getSelectedSaleStep());
            quoteServiceAsync.save(sale, sale.getProductsServices(), new IabakoAsyncCallback<Sale>() {
                public void onSuccess(Sale result) {
                    saleSuccessCallback(result);
                    super.onSuccess(result);
                }
            });
        } else {
            saleServiceAsync.save(isDraft, sale, sale.getProductsServices(), sale.getPayments(), installments, new IabakoAsyncCallback<Sale>() {
                public void onSuccess(Sale result) {
                    //This is because installments lazy relations is gwt transient
                    result.setInstallments(sale.getInstallmentsDefinition() != null ? sale.getInstallmentsDefinition().getInstallments() : null);
                    saleSuccessCallback(result);
                    super.onSuccess(result);
                }
            });
        }
        return true;
    }

    private void saleSuccessCallback(Sale sale) {

        AuthenticateUserProvider.getBalanceAccount().refresh();

        if (NavigationController.getInstance().getRootPage().pageContentComposite instanceof SaleSearch) {
            //If rootPage is SaleSearch, we have to refresh search
            ((SaleSearch) NavigationController.getInstance().
                    getRootPage().pageContentComposite).saleSearchForm.searchForm.search(false);
        } else if (sale == null) {
            //Sale en masse
            Menu.unselectMenus();
            Menu.selectMenu(MenuItemEnum.Sales);
            NavigationController.getInstance().goTo(PageEnum.SaleSearch);
        }

        if (sale != null && !GenericTools.isEmpty(sale.getInstallments()) && sale.getStatus() != FinancialStatusSale._0_draft) {

            AuthenticateUserProvider.getNotificationPanel().refresh(
                    NotificationType.stockLowerThanLimit, NotificationType.stockRupture, NotificationType.futureInstallments, NotificationType.newPastInstallments);
            AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.saleNew, TrackingType.quoteTransformed);

            NavigationController.getInstance().goTo(PageEnum.SaleModify, sale.getId() + "", "fromSaleCreation");
            return;
        }

        if (!quoteMode && sale != null && sale.getStatus() != FinancialStatusSale._0_draft) {
            NavigationController.getInstance().goTo(PageEnum.SaleModify, sale.getId() + "", "fromSaleCreation");

            AuthenticateUserProvider.getNotificationPanel().refresh(NotificationType.stockLowerThanLimit, NotificationType.stockRupture);
            AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.saleNew, TrackingType.quoteTransformed);
            return;
        }

        if (sale != null && quoteMode && quoteTransformToSaleMode) {
            goToTransformQuoteForm(sale);
            return;
        }

        popup.handleClickClose();

        if (quoteMode && sale != null) {
            ValidationMessage validationMessageInPageContent = ValidationMessage.getFromRootPage();
            switch (sale.getStatus()) {
                case _0_quote_draft:
                    validationMessageInPageContent.showSuccessMessage("validation_sale_form_quote_save_success");
                    AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.quoteDraft);
                    break;
                case _3_quote_discard:
                    validationMessageInPageContent.showSuccessMessage("validation_sale_form_quote_discard_success");
                    AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.quoteDiscard);
                    break;
            }
            return;
        }

        if (sale == null) {
            //Sale en masse
            ValidationMessage validationMessageInPageContent = ValidationMessage.getFromRootPage();
            validationMessageInPageContent.showSuccessMessage("validation_sale_form_en_masse_success");

            AuthenticateUserProvider.getNotificationPanel().refresh(NotificationType.stockLowerThanLimit, NotificationType.stockRupture);
            AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.saleNew);

        } else {
            //Sale draft
            ValidationMessage validationMessageInPageContent = ValidationMessage.getFromRootPage();
            validationMessageInPageContent.showSuccessMessage("validation_sale_form_draft_success");

            AuthenticateUserProvider.getTrackingPanel().refresh(TrackingType.saleDraft, TrackingType.quoteTransformed);
        }
    }

    private void goToTransformQuoteForm(Sale sale) {
        NavigationController.getInstance().closePopup(false);

        //Restore sale draft from quote
        sale.setQuote(false);
        sale.setSaleQuoteId(sale.getId() + "");
        sale.setPaymentType(PaymentType.onePayment);
        sale.setSaleStep(SaleStep.how);
        sale.setStatus(FinancialStatusSale._0_draft);

        final SaleMake saleMake = new SaleMake(this);
        saleMake.restoreDraft(sale);

        saleServiceAsync.getNextAvailableId(new IabakoAsyncCallback<String>() {
            public void onSuccess(String result) {
                saleMake.saleMakeConfirmation.number.setValue(result);
                NavigationController.getInstance().goTo(saleMake);
                super.onSuccess(result);
            }
        });
    }

    private boolean saveEnMasseProcess(final Sale sale) {
        saleEnMasseServiceAsync.save(clientList, sale, sale.getProductsServices(),
                sale.getPayments(), sale.getInstallments(), new IabakoAsyncCallback<Void>() {
            public void onSuccess(Void result) {
                saleSuccessCallback(null);
                super.onSuccess(result);
            }
        });
        return true;
    }
}
